﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using GRMS.App_Data;
using System.IO;
using System.Xml.Serialization;
using System.Globalization;
using System.Runtime.Serialization;
using System.Xml;

namespace GRMS.App_Code
{
    public class Session
    {
        #region Machine Initalization
        public Session(string GM, string Dir)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            OptionStatements = new Dictionary<string, Hashtable>();
            this.rootDir = Dir;
            int user = Int32.Parse(new UserGetList(db, GM)[0].USERID.ToString());
            this.UserID = user;
            this.RuleSetID = 1;

            Hashtable Vars = new Hashtable();
            db.UpdateRoomByID(0, this.UserID, this.RuleSetID, Serializer(Vars, typeof(Hashtable)));
            db.SubmitChanges();

            RoomGetGMList list = new RoomGetGMList(db, this.UserID);
            this.RoomID = Int32.Parse(list[list.Count - 1].RoomID.ToString());
        }
        public Session(int roomID, string Dir)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            RoomGet result = new RoomGetList(db, RoomID)[0];

            OptionStatements = new Dictionary<string, Hashtable>();
            this.rootDir = Dir;
            int user = 1; // Int32.Parse(new UserGetList(db, GM)[0].USERID.ToString());
            this.UserID = user;
            this.RuleSetID = 1;

            Room thisRoom = new Room();
            Hashtable Vars = new Hashtable();
            thisRoom.userID = this.UserID;
            thisRoom.ruleSetID = this.RuleSetID;
            StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
            OrderedTable orderedTable = new OrderedTable(Vars);
            XmlSerializer serializer = new XmlSerializer(typeof(OrderedTable));
            serializer.Serialize(writer, orderedTable);
            thisRoom.Attributes = writer.ToString();
            db.Rooms.InsertOnSubmit(thisRoom);
            db.SubmitChanges();
        }
        #endregion

        #region Session Variables
        //Root Directory
        private string rootDir;
        public string Root
        {
            get { return rootDir; }
            set { rootDir = value; }
        }
        private object rootId;
        public object RootID
        {
            get { return rootId; }
            set { rootId = value; }
        }
        private int RoomID;
        public int roomID { get { return RoomID; } set { RoomID = value; } }
        private int UserID;
        private int RuleSetID;
        private Dictionary<string, Hashtable> OptionStatements;
        private ActionSequencer CombatList;
        private ActionEventRequest AER;
        #endregion

        #region Session Methods
        //GlobalVars Access
        private Hashtable GetRoomAttributeHt()
        {
            GameSessionDataContext db = new GameSessionDataContext();
            XmlSerializer serializer = new XmlSerializer(typeof(OrderedTable));
            RoomGet result = new RoomGetList(db, RoomID)[0];
            MemoryStream ms = new MemoryStream(Encoding.UTF32.GetBytes(result.Attributes.Replace(" encoding=\"utf-16\"", "")));

            OrderedTable orderedTable;
            try
            {
                orderedTable = (OrderedTable)serializer.Deserialize(ms);
            }
            catch
            {
                return new Hashtable();
            }
            return orderedTable.HashTable;
        }
        private bool SaveRoom(Hashtable ht)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            try
            {
                StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
                XmlSerializer serializer = new XmlSerializer(typeof(OrderedTable));
                serializer.Serialize(writer, new OrderedTable(ht));

                db.UpdateRoomByID(this.RoomID, this.UserID, this.RuleSetID, writer.ToString());
                db.SubmitChanges();

                Transaction("Update", "Room");
                return true;
            }
            catch (Exception e) { return false; }
        }
        public object GetGlobal(string key)
        {
            if (key == "CombatList")
                return CombatList;
            else if (key == "ActionEventRequest")
                return AER;

            return GetRoomAttributeHt()[key];
        }
        public bool SetGlobal(string key, object value)
        {
            Hashtable GlobalVars = GetRoomAttributeHt();

            try
            {
                if (GlobalVars.ContainsKey(key))
                    GlobalVars[key] = value;
                else if (key == "CombatList")
                    CombatList = (ActionSequencer)value;
                else if (key == "ActionEventRequest")
                    AER = (ActionEventRequest)value;
                else
                    GlobalVars.Add(key, value);
            }
            catch { return false; }

            return SaveRoom(GlobalVars);
        }
        public bool CheckGlobal(string key)
        {
            return GetRoomAttributeHt().ContainsKey(key);
        }
        public string ResetGlobal(string ID)
        {
            try
            {
                GameSessionDataContext db = new GameSessionDataContext();
                ExtUserGetList list = new ExtUserGetList(db, ID.GetHashCode().ToString());
                this.UserID = list[0].USERID ?? default(int);

                Hashtable Vars = new Hashtable();
                db.UpdateRoomByID(0, this.UserID, this.RuleSetID, Serializer(Vars, typeof(Hashtable)));
                db.SubmitChanges();
                Transaction("SStart", "GM:" + this.UserID + "; RoomID=" + this.RoomID);
                return "Session Start: GM:" + this.UserID + "; RoomID=" + this.RoomID;
            }
            catch { return "Failed to Create Session"; }
        }
        public string CloseSession(string ID)
        {
            if ((int)new GameSessionDataContext().OpenSessionCheck(this.RoomID).ReturnValue == 0)
            {
                GameSessionDataContext db = new GameSessionDataContext();
                ExtUserGetList list = new ExtUserGetList(db, ID.GetHashCode().ToString());
                this.UserID = list[0].USERID ?? default(int);

                Transaction("SEnd", "GM:" + this.UserID + "; RoomID=" + this.RoomID);
                return "Session Closed GM:" + this.UserID + "; RoomID=" + this.RoomID;
            }
            else
                return "Failed to Close Session";
        }
        public string Subscribe(string ID, string roomID)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            ExtUserGetList list = new ExtUserGetList(db, ID.GetHashCode().ToString());
            this.UserID = list[0].USERID ?? default(int);

            this.RoomID = Int32.Parse(roomID);

            Transaction("Subscribe: ", "Username: " + this.UserID + "; RoomID=" + this.RoomID);
            return "Subscribe: Username: " + this.UserID + "; RoomID=" + this.RoomID;
        }

        //OptionStatements Access
        public bool CheckOptionStatements(string key)
        {
            return OptionStatements.ContainsKey(key);
        }
        public Hashtable GetOptionStatements(string key)
        {
            return OptionStatements[key];
        }
        public bool SetOptionStatements(string key, Hashtable value)
        {
            try
            {
                if (CheckOptionStatements(key))
                    OptionStatements[key] = value;
                else
                    OptionStatements.Add(key, value);
            }
            catch (Exception e)
            {
                return false;
            }
            return true;
        }

        //Character/NPC append/remove/get functions
        public Character GetCharacter(string id)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            try
            {
                var serializer = new NetDataContractSerializer();
                string result = new CharacterGetList(db, id)[0].Attributes;
                MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(result.Replace(" encoding=\"utf-16\"", "")));
                XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());

                Character c = (Character)serializer.ReadObject(reader, true);

                stream.Close();
                return c;
            }
            catch { return null; }
        }
        public bool SaveCharacter(Character c)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            try
            {
                var serializer = new NetDataContractSerializer();
                StringBuilder stringBuilder = new StringBuilder();
                using (XmlWriter writer = XmlWriter.Create(stringBuilder))
                {
                    serializer.WriteObject(writer, c);
                }

                CharacterGet gotten = new CharacterGetList(db, c.ID)[0];
                db.UpdateCharacterByID(Int32.Parse(gotten.CharID.ToString()), Int32.Parse(gotten.UserID.ToString()), c.ID, stringBuilder.ToString());
                db.SubmitChanges();

                Transaction("Save", "Character" + c.ID);
                return true;
            }
            catch { return false; }
        }
        public bool AddCharacter(Character c, string user)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            if (!HasCharacter(c.ID))
            {
                var serializer = new NetDataContractSerializer();
                StringBuilder stringBuilder = new StringBuilder();
                using (XmlWriter writer = XmlWriter.Create(stringBuilder))
                {
                    serializer.WriteObject(writer, c);
                }

                int userID = Int32.Parse(new UserGetList(db, user)[0].USERID.ToString());

                db.UpdateCharacterByID(0, userID, c.ID, stringBuilder.ToString());
                db.SubmitChanges();

                Transaction("Join", "Character" + c.ID);
                return true;
            }
            else if (HasCharacter(c.ID))
            {
                var serializer = new NetDataContractSerializer();
                StringBuilder stringBuilder = new StringBuilder();
                using (XmlWriter writer = XmlWriter.Create(stringBuilder))
                {
                    serializer.WriteObject(writer, c);
                }

                CharacterGet gotten = new CharacterGetList(db, c.ID)[0];                
                db.UpdateCharacterByID(Int32.Parse(gotten.CharID.ToString()), Int32.Parse(gotten.UserID.ToString()), c.ID, stringBuilder.ToString());
                db.SubmitChanges();

                Transaction("Update", "Character" + c.ID);
                return true;
            }
            return false;
        }
        public bool RemoveCharacter(string id)
        {
            try
            {
                Character c = GetCharacter(id);
                Transaction("Leave", "Character" + id);
                return true;
            }
            catch { return false; }
        }
        public bool HasCharacter(string id)
        {
            if (new CharacterGetList(new GameSessionDataContext(), id).Count > 0)
                return true;
            return false;
        }

        private bool Transaction(string TransName, string Notes)
        {
            try
            {
                GameSessionDataContext db = new GameSessionDataContext();
                db.AddTransaction(RoomID, UserID, RuleSetID, TransName, Notes);
                db.SubmitChanges();
                return true;
            }
            catch { return false; }
        }
        #endregion

        #region Serialization Helpers
        private string Serializer(object o, Type T)
        {
            StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);

            switch (T.ToString())
            {
                case "System.Collections.Hashtable":
                    XmlSerializer serializer = new XmlSerializer(typeof(OrderedTable));
                    serializer.Serialize(writer, new OrderedTable((Hashtable)o));
                    break;
                case "GRMS.Character":
                    serializer = new XmlSerializer(typeof(Character));
                    serializer.Serialize(writer, (Character)o);
                    break;
                default:
                    serializer = new XmlSerializer(T);
                    serializer.Serialize(writer, "");
                    break;
            }

            return writer.ToString();
        }

        private object Deserializer(Type T, string id)
        {
            GameSessionDataContext db = new GameSessionDataContext();
            XmlSerializer serializer = new XmlSerializer(typeof(String)); ;
            string s = "";

            switch (T.ToString())
            {
                case "OrderedTable":
                    serializer = new XmlSerializer(typeof(OrderedTable));
                    RoomGet result = new RoomGetList(db, RoomID)[0];
                    s = result.Attributes;
                    break;
                case "Character":
                    serializer = new XmlSerializer(typeof(Character));
                    s = new CharacterGetList(db, id)[0].Attributes;
                    break;
                default:
                    break;
            }
            MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(s));

            return serializer.Deserialize(ms);
        }
        #endregion
    }

    [Serializable()]
    public class OrderedTable : List<NameValuePair>
    {
        private Dictionary<string, Attribute> properties;

        public OrderedTable() { }
        public OrderedTable(Hashtable table)
        {
            IDictionaryEnumerator enumerator = table.GetEnumerator();
            while (enumerator.MoveNext())
                this.AddIfNotExisting(Convert.ToString(enumerator.Key), enumerator.Value);
        }
        public OrderedTable(Dictionary<string, Attribute> properties)
        {
            IDictionaryEnumerator enumerator = properties.GetEnumerator();
            while (enumerator.MoveNext())
                this.AddIfNotExisting(Convert.ToString(enumerator.Key), enumerator.Value);
        }

        private void AddIfNotExisting(string key, object value)
        {
            if (!this.Contains(new NameValuePair(key, value)))
                this.Add(new NameValuePair(key, value));
        }

        public void Add(string key, object value)
        {
            base.Add(new NameValuePair(key, value));
        }

        public object this[string key]
        {
            get
            {
                object valueObject = null;
                NameValuePair pair = this.Find(
                    delegate(NameValuePair nvp)
                    {
                        return nvp.Name == key;
                    });
                if (pair != null)
                    valueObject = pair.Value;
                return valueObject;
            }
            set
            {
                NameValuePair pair = this.Find(
                    delegate(NameValuePair nvp)
                    {
                        return nvp.Name == key;
                    });
                if (pair != null)
                    pair.Value = value;
            }
        }

        public Hashtable HashTable
        {
            get
            {
                Hashtable table = new Hashtable();
                foreach (NameValuePair pair in this)
                    if (!table.ContainsKey(pair.Name))
                        table.Add(pair.Name, pair.Value);
                return table;
            }
        }
        public Dictionary<string, Attribute> Dictionary
        {
            get
            {
                Dictionary<string, Attribute> table = new Dictionary<string, Attribute>();
                foreach (NameValuePair pair in this)
                    if (!table.ContainsKey(pair.Name))
                        table.Add(pair.Name, (Attribute)pair.Value);
                return table;
            }
        }
    }
    [Serializable()]
    public class NameValuePair
    {
        public string Name { get; set; }
        public object Value { get; set; }

        public NameValuePair() { }
        public NameValuePair(string name, object value)
        {
            this.Name = name;
            this.Value = value;
        }
    }

    public class UserGet
    {
        private Int32? _USERID;
        public UserGet(Int32? AUSERID)
        { _USERID = AUSERID; }
        public Int32? USERID { get { return _USERID; } }
    }
    public class UserGetList : List<UserGet>
    {
        public UserGetList(GameSessionDataContext db, string id)
        {
            var query =
                from Users in db.Users
                where id == Users.UserName
                select new { Users.USERID };
            foreach (var r in query)
                Add(new UserGet( r.USERID));
        }
    }
    public class ExtUserGetList : List<UserGet>
    {
        public ExtUserGetList(GameSessionDataContext db, string extID)
        {
            var query =
                from Users in db.Users
                where Int64.Parse(extID) == Users.extID
                select new { Users.USERID };
            foreach (var r in query)
                Add(new UserGet(r.USERID));
        }
    }
    public class RoomGet
    {
        private Int32? _RoomID;
        private Int32? _UserID;
        private Int32? _RuleSetID;
        private String _Attributes;
        public RoomGet(Int32? ARoomID, Int32? AUserID, Int32? ARuleSetID, String AAttributes)
        {
            _RoomID = ARoomID;
            _UserID = AUserID;
            _RuleSetID = ARuleSetID;
            _Attributes = AAttributes;
        }
        public Int32? RoomID { get { return _RoomID; } }
        public Int32? UserID { get { return _UserID; } }
        public Int32? RuleSetID { get { return _RuleSetID; } }
        public String Attributes { get { return _Attributes; } }
    }
    public class RoomGetList : List<RoomGet>
    {
        public RoomGetList(GameSessionDataContext db, int id)
        {
            var query =
                from Rooms in db.Rooms
                where id == Rooms.RoomID
                select new
                {
                    Rooms.RoomID,
                    Rooms.userID,
                    Rooms.ruleSetID,
                    Rooms.Attributes
                };
            foreach (var r in query)
                Add(new RoomGet(r.RoomID, r.userID, r.ruleSetID, r.Attributes));
        }
    }
    public class RoomGetGMList : List<RoomGet>
    {
        public RoomGetGMList(GameSessionDataContext db, int id)
        {
            var query =
                from Rooms in db.Rooms
                where id == Rooms.userID
                select new
                {
                    Rooms.RoomID,
                    Rooms.userID,
                    Rooms.ruleSetID,
                    Rooms.Attributes
                };
            foreach (var r in query)
                Add(new RoomGet(r.RoomID, r.userID, r.ruleSetID, r.Attributes));
        }
    }
    public class CharacterGet
    {
        private Int32? _CharID;
        private Int32? _UserID;
        private String _CharacterName;
        private String _Attributes;
        public CharacterGet(Int32? ACharID, Int32? AUserID, String ACharacterName, String AAttributes)
        {
            _CharID = ACharID;
            _UserID = AUserID;
            _CharacterName = ACharacterName;
            _Attributes = AAttributes;
        }
        public Int32? CharID { get { return _CharID; } }
        public Int32? UserID { get { return _UserID; } }
        public String CharacterName { get { return _CharacterName; } }
        public String Attributes { get { return _Attributes; } }
    }
    public class CharacterGetList : List<CharacterGet>
    {
        public CharacterGetList(GameSessionDataContext db, string id)
        {
            var query =
                from Characters in db.Characters
                where id == Characters.CharacterName
                select new
                {
                    Characters.CharID,
                    Characters.userID,
                    Characters.CharacterName,
                    Characters.Attributes
                };
            foreach (var r in query)
                Add(new CharacterGet(r.CharID, r.userID, r.CharacterName, r.Attributes));
        }

        public CharacterGetList(GameSessionDataContext db, int id)
        {
            var query =
                from Characters in db.Characters
                where id == Characters.userID
                select new
                {
                    Characters.CharID,
                    Characters.userID,
                    Characters.CharacterName,
                    Characters.Attributes
                };
            foreach (var r in query)
                Add(new CharacterGet(r.CharID, r.userID, r.CharacterName, r.Attributes));
        }

    }
    public class TransactionGet
    {
        private Int32? _TransactionID;
        private Int32? _RoomID;
        private Int32? _UserID;
        private Int32? _RuleSetID;
        private String _Trx;
        private String _Attributes;
        public TransactionGet(Int32? ATransactionID, Int32? ARoomID, Int32? AUserID, Int32? ARuleSetID, String ATrx, String AAttributes)
        {
            _TransactionID = ATransactionID;
            _RoomID = ARoomID;
            _UserID = AUserID;
            _RuleSetID = ARuleSetID;
            _Trx = ATrx;
            _Attributes = AAttributes;
        }
        public Int32? TransactionID { get { return _TransactionID; } }
        public Int32? RoomID { get { return _RoomID; } }
        public Int32? UserID { get { return _UserID; } }
        public Int32? RuleSetID { get { return _RuleSetID; } }
        public String Trx { get { return _Trx; } }
        public String Attributes { get { return _Attributes; } }
    }
    public class TransactionGetList : List<TransactionGet>
    {
        public TransactionGetList(GameSessionDataContext db, int id)
        {
            var query =
                from RoomTransactions in db.RoomTransactions
                where id == RoomTransactions.roomID
                select new
                {
                    RoomTransactions.TransactionID,
                    RoomTransactions.roomID,
                    RoomTransactions.userID,
                    RoomTransactions.ruleSetID,
                    RoomTransactions.trx,
                    RoomTransactions.Attributes
                };
            foreach (var r in query)
                Add(new TransactionGet(r.TransactionID, r.roomID, r.userID, r.ruleSetID, r.trx, r.Attributes));
        }
    }
}